package com.example.service.Impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.common.enums.KeyEnum;
import com.example.common.enums.RedisFileConstant;
import com.example.common.websocket.WebSocketServe;
import com.example.context.BaseContext;
import com.example.mapper.ApprovalMapper;
import com.example.mapper.ProjectMapper;
import com.example.mapper.TemplateMapper;
import com.example.pojo.dto.AccessDTO;
import com.example.pojo.dto.ProjecttDTO;
import com.example.pojo.entity.*;
import com.example.pojo.vo.ProjectVO;
import com.example.service.*;
import com.example.utils.AddressUtils;
import com.example.utils.FiltersUtils;
import com.example.utils.HttpsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * TODO
 *
 * @author ding
 * @since 2024/5/31
 */
@Service
@DS("master")
public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> implements ProjectService {

    @Autowired
    private ProjectMapper projectMapper;

    @Autowired
    private EntrustService entrustService;

    @Autowired
    private TeamService teamService;

    @Autowired
    private ApprovalMapper approvalMapper;

    @Autowired
    private AdminService adminService;

    @Autowired
    private MarkerService markerService;

    @Autowired
    private ContractService contractService;

    @Autowired
    private TemplateMapper templateMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WebSocketServe webSocketServe;

    /**
     * 根据项目名称获取项目信息
     */
    public Project selectByName(String projectName){
        QueryWrapper<Project> queryWrapper = new QueryWrapper<>();
        if(projectName != null){
            queryWrapper.like(StrUtil.isNotBlank(projectName),"name",projectName)
                        .eq("is_deleted",0);
            return getOne(queryWrapper);
        }
        return null;
    }

    /**
     * 分页获取项目信息
     */
    public Page<Project> selectPage(String name, Integer entrustId, Integer teamId, Boolean access, LocalDate begin, LocalDate end, String statuses, Integer pageNum, Integer pageSize) {
        QueryWrapper<Project> queryWrapper = new QueryWrapper<>();

        List<Integer> status = new ArrayList<>();
        if (statuses != null && statuses.length() > 2){
            status = FiltersUtils.getFilters(statuses);
        }

        queryWrapper.like(StrUtil.isNotBlank(name),"name",name)
                    .eq("is_deleted",0)
                    .eq(ObjectUtil.isNotEmpty(entrustId), "entrust_id", entrustId)
                    .eq(ObjectUtil.isNotEmpty(teamId), "team_id", teamId)
                    .gt(ObjectUtil.isNotEmpty(begin), "begin", begin)
                    .lt(ObjectUtil.isNotEmpty(end), "end", end)
                    .in(status.size() > 0 ,"status", status);

        //如果access存在，说明当前页面为接取项目页面，只展示未被其他团队接取且通过审批的项目的项目信息
        if (!ObjectUtil.isEmpty(access)){

            Page<Project> page = projectMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
            List<Project> records = page.getRecords();
            records.forEach(project -> project.setEntrustName(entrustService.getById(project.getEntrustId()).getName()));
            List<Project> projectList = records.stream()
                    .filter(project -> project.getTeamId() == null)
                    .filter(project -> project.getStatus() == 1)
                    .collect(Collectors.toList());
            page.setRecords(projectList);
            return page;
        }

        Page<Project> page = projectMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
        List<Project> records = page.getRecords();
        for (Project record : records) {
            record.setEntrustName(entrustService.getById(record.getEntrustId()).getName());
            if (record.getTeamId() != null){
                record.setTeamName(teamService.getById(record.getTeamId()).getName());
            }
        }
        page.setRecords(records);

        return page;
    }

    /**
     * 新增项目信息并生成审批信息
     */
    @Transactional
    public void addProject(Project project) {
        project.setEntrustId(1);
        project.setStatus(0);
        project.setIsDeleted(0);
        project.setVersion(0);

        projectMapper.insert(project);
        redisTemplate.delete(KeyEnum.PROJECT_UNDE_UNCOMP);

        redisTemplate.opsForSet().add(RedisFileConstant.upload_db_file, project.getImg());

        Integer projectId = project.getId();

        //获取当前登录id
        Long currentId = BaseContext.getCurrentId();
        Admin admin = adminService.selectById(Math.toIntExact(currentId));
        Template template = templateMapper.selectOne(new LambdaQueryWrapper<Template>().eq(Template::getName, "项目审批"));
        String person = template.getPerson();
        StringBuilder sb = new StringBuilder("");
        String[] split = person.split(",");
        for (int i = 1; i <= split.length; i++) {
            Admin adm = adminService.selectById(Integer.valueOf(split[i - 1]));
            sb.append(adm.getName()).append("-");
        }
        String variables = sb.toString();
        Map<String, Object> map = new HashMap<>();
        map.put("authenticate", admin.getName());
        map.put("proDefId", template.getProcDefId());
        map.put("variables", variables);
        map.put("projectId", projectId);
        map.put("UID", admin.getId());

        String procInstId = HttpsUtil.sendPost("http://localhost:9091/engine/start", map);

        Approval approval = Approval.builder()
                .projectId(project.getId())
                .applicantId(admin.getId())
                .status("0")
                .type("项目申请")
                .procInstId(procInstId)
                .build();
        approvalMapper.insert(approval);

        //通过websocket向客户端浏览器推送消息，推送数据格式为JSON type approval content
        Map webMap = new HashMap();
        //审批类型 1：项目审批 2：采购审批 3：入队审批
        webMap.put("type", 1);
        webMap.put("authenticate", admin.getName());
        webMap.put("procInstId", procInstId);
        webMap.put("content", "项目名称:" + project.getName());
        String json = JSON.toJSONString(webMap);
        webSocketServe.sendToClient(json, split[0]);
        //将待审批业务放于redis中
        redisTemplate.opsForSet().add(KeyEnum.TASK + "_" + split[0], procInstId);
    }

    /**
     * 更新项目信息
     */
    @Transactional
    public void updateProject(Project project) {
        //前端传递的图片数据可能有异常
        if (project.getImg().equals("h")){
            project.setImg(null);
        }
        projectMapper.updateById(project);
        redisTemplate.opsForSet().add(RedisFileConstant.upload_db_file, project.getImg());
        redisTemplate.delete(KeyEnum.PROJECT_UNDE_UNCOMP);
    }

    /**
     * 删除选中的信息
     */
    @Transactional
    public void deleteById(Integer id) {
        QueryWrapper<Project> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",id);
        Project project = getById(id);
        project.setIsDeleted(1);
        projectMapper.update(project,queryWrapper);
        redisTemplate.delete(KeyEnum.PROJECT_UNDE_UNCOMP);
    }

    /**
     * 批量删除选择的项目信息
     */
    @Transactional
    public void delBatch(List<Integer> ids) {
        for (Integer id : ids) {
            deleteById(id);
        }
    }

    /**
     * 根据当前登录账户的所属施工队id获取对应项目信息
     */
    public List<ProjectVO> getProjectListByUserId() {
        Admin admin = adminService.selectById(Math.toIntExact(BaseContext.getCurrentId()));
        Integer teamId = adminService.selectById(admin.getId()).getTeamId();
        if (teamId != null){
            QueryWrapper<Project> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("team_id",teamId);
            List<Project> projectList = list(queryWrapper);
            List<ProjectVO> projectVOS = new ArrayList<>();
            for (Project project : projectList) {
                ProjectVO projectVO = ProjectVO.builder()
                        .description(project.getDescription())
                        .label(project.getName())
                        .value(project.getId())
                        .img(project.getImg())
                        .build();
                projectVOS.add(projectVO);
            }
            return projectVOS;
        }
        return null;
    }

    /**
     * 根据id获取对应信息
     */
    public Project selectById(Integer id) {
        Project project = projectMapper.selectById(id);
        Marker marker = markerService.getByProjectId(id);
        if(!ObjectUtil.isEmpty(marker)){
            project.setAddress(AddressUtils.getAddressByHttp(marker.getLng(), marker.getLat()));
        }
        project.setEntrustName(entrustService.getById(project.getEntrustId()).getName());
        if (project.getTeamId() != null){
            project.setTeamName(teamService.getById(project.getTeamId()).getName());
        }
        return project;
    }

    /**
     * 施工团队接取新的项目
     */
    @Transactional
    public void accessProject(AccessDTO accessDTO) throws IOException {
        //项目接取后 存在施工团队接取该项目 项目状态变为进行中
        projectMapper.update(new LambdaUpdateWrapper<Project>()
                .set(Project::getTeamId, accessDTO.getTeamId())
                .set(Project::getStatus, 3)
                .eq(Project::getId, accessDTO.getProjectId()));

        redisTemplate.delete(KeyEnum.PROJECT_UNDE_UNCOMP);

        //团队接取新的项目时，生成一个新的合同信息
        contractService.add(accessDTO);
    }

    /**
     * 获取指定时间段中的项目信息
     *
     * @param currentYear  年份
     * @param currentMonth 月份
     * @return
     */
    public List<Map<String, Object>> getProjectInfoByDate(Integer currentYear, Integer currentMonth) {
        //起始值   2024-02-01  2024-07-01
        LocalDate begin = LocalDate.of(currentYear, currentMonth , 1);
        //终止值   2024-02-28  2024-07-30      获取当前月份的最后一天
        LocalDate end = begin.with(TemporalAdjusters.lastDayOfMonth());

        QueryWrapper<Project> beginWrapper = new QueryWrapper<>();
        beginWrapper.between("begin", begin, end);
        //获取到开始日期在该时间段中的项目信息
        List<Project> beginProject = projectMapper.selectList(beginWrapper);
        List<Map<String, Object>> collect1 = beginProject.stream().map(project -> {
            Map<String, Object> map = new HashMap<>();
            map.put("date", project.getBegin().getDayOfMonth());
            map.put("projectName", project.getName());
            map.put("status", 0);
            return map;
        }).collect(Collectors.toList());

        QueryWrapper<Project> endWrapper = new QueryWrapper<>();
        endWrapper.between("end", begin, end);
        //获取到截止日志在该时间段的项目信息
        List<Project> endProject = projectMapper.selectList(endWrapper);
        List<Map<String, Object>> collect2 = endProject.stream().map(project -> {
            Map<String, Object> map = new HashMap<>();
            map.put("date", project.getEnd().getDayOfMonth());
            map.put("projectName", project.getName());
            map.put("status", 1);
            return map;
        }).collect(Collectors.toList());

        List<Map<String, Object>> collect = Stream.concat(collect1.stream(), collect2.stream()).collect(Collectors.toList());

        return collect;
    }

    /**
     * 按项目阶段进行预算权重分配  2 3 4
     */
    public void allotBudget(Double budget) {
        Double[] weight = new Double[]{2D, 3D, 4D};
        //已分配阶段数
        Integer[] number = new Integer[weight.length];
        for (int i = 0; i < budget; i++) {
            //当前权重
            Double[] current = new Double[weight.length];
            for (int w = 0; w < weight.length; w++) {
                current[w] = weight[w] / (number[w] == null ? 1 : number[w]);
            }
            int index = 0;
            Double currentMax = current[0];
            for (int d = 1; d < current.length; d++) {
                //考虑全等的情况
                Boolean isTrue = true;
                while (isTrue) {
                    Set set = new HashSet();
                    for (Double c : current) {
                        set.add(c);
                    }
                    if (set.size() == 1) {//代表全等
                        for (int e = 0; e < current.length; e++) {
                            current[e] = current[e] * Math.random();
                        }
                    } else {
                        isTrue = false;
                    }
                }
                //比较所有的数,寻找出下标最大的哪一位
                if (currentMax < current[d]) {
                    currentMax = current[d];
                    index = d;
                }
            }
            number[index] = number[index] == null ? 1 : number[index] + 1;
        }
        for (Integer i : number) {
            System.err.println(i);
        }

    }
}
